import java.util.ArrayList;

public class AnalyseParse {
    private ArrayList<Pairs> tokens;
    //private ArrayList<Pairs> parseTree;
    private Node firstNode;
    private Node currentNode;
    private Node nextNode;
    private static int index = 0;
    private Pairs current;
    private Pairs next;
    private Pairs nNext;
    private ArrayList<Pairs> error;

    public AnalyseParse(ArrayList<Pairs> tokens) {
        this.tokens = tokens;
        //this.parseTree = new ArrayList<>();
        this.error = new ArrayList<>();
    }

    public ArrayList<Pairs> getError() {
        return error;
    }

    public void getSym() {
        if (index < tokens.size()) {
            this.current = tokens.get(index);
            if (index + 1 < tokens.size()) {
                this.next = tokens.get(index + 1);
                if (index + 2 < tokens.size()) {
                    this.nNext = tokens.get(index + 2);
                } else {
                    this.nNext = null;
                }
            } else {
                this.next = null;
                this.nNext = null;
            }
            index++;
        } else {
            this.current = null;
            this.next = null;
            this.nNext = null;
        }
    }

    public Node analyseTokens() {
        getSym();
        currentNode = CompUnit();
        return currentNode;
    }

    public Node CompUnit() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        while ((this.current.getKind().equals(Pairs.Kind.CONSTTK) || (this.current.getKind().equals(Pairs.Kind.INTTK)))
                && (!this.next.getKind().equals(Pairs.Kind.MAINTK)) && (!this.nNext.getKind().equals(Pairs.Kind.LPARENT))) {
            sons.add(Decl());
        }
        while ((this.current.getKind().equals(Pairs.Kind.VOIDTK) || this.current.getKind().equals(Pairs.Kind.INTTK)) && (!this.next.getKind().equals(Pairs.Kind.MAINTK))) {
            sons.add(FuncDef());
        }
        //MainFuncDef();
        sons.add(MainFuncDef());
        /*Node logNode = currentNode;
        Node newNode = MainFuncDef();
        currentNode = logNode;
        currentNode.addChild(newNode);*/
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "CompUnit", line);
        //this.parseTree.add(pairs);
        return new Node(pairs, sons);
    }

    public Node Decl() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.CONSTTK)) {
            sons.add(ConstDecl());
        } else if (this.current.getKind().equals(Pairs.Kind.INTTK)) {
            sons.add(VarDecl());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "Decl", line);
        //this.parseTree.add(pairs);
        return new Node(pairs, sons);
    }

    public Node ConstDecl() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.CONSTTK)) {
            sons.add(new Node(current, null));
            getSym();
            sons.add(BType());
            sons.add(ConstDef());
            while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(ConstDef());
            }

            if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                //Pairs pairs = new Pairs(Pairs.Kind.PARSE, "ConstDecl",this.current.getLine());
                //this.parseTree.add(pairs);
                sons.add(new Node(current, null));
                getSym();
            } else {
                sons.add(new Node(current, null));
                Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                error.setLine(this.tokens.get(index - 2).getLine());
                this.error.add(error);
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "ConstDecl", line);
        return new Node(pairs, sons);
    }

    public Node BType() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.INTTK)) {
            sons.add(new Node(current, null));
            getSym();
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "BType", line);
            //this.parseTree.add(pairs);
            return new Node(pairs, sons);
        }
        return null;
    }

    public Node ConstDef() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.IDENFR)) {
            sons.add(new Node(current, null));
            getSym();
            //Ident
            while (this.current.getKind().equals(Pairs.Kind.LBRACK)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(ConstExp());
                if (this.current.getKind().equals(Pairs.Kind.RBRACK)) {
                    sons.add(new Node(current, null));
                    getSym();
                    continue;
                } else {
                    sons.add(new Node(current, null));
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "k");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                }
            }
            if (this.current.getKind().equals(Pairs.Kind.ASSIGN)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(ConstInitVal());
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "ConstDef", line);
        return new Node(pairs, sons);
    }

    public Node ConstInitVal() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.LBRACE)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.RBRACE)) {
                sons.add(new Node(current, null));
                getSym();
            } else {
                sons.add(ConstInitVal());
                while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
                    sons.add(new Node(current, null));
                    getSym();
                    sons.add(ConstInitVal());
                }
                if (this.current.getKind().equals(Pairs.Kind.RBRACE)) {
                    sons.add(new Node(current, null));
                    getSym();
                }
            }
        } else {
            sons.add(ConstExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "ConstInitVal", line);
        return new Node(pairs, sons);
    }

    public Node VarDecl() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(BType());
        sons.add(VarDef());
        while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
            sons.add(new Node(current, null));
            getSym();
            sons.add(VarDef());
        }
        if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
            sons.add(new Node(current, null));
            getSym();
        } else {
            Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
            error.setLine(this.tokens.get(index - 2).getLine());
            this.error.add(error);
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "VarDecl", line);
        return new Node(pairs, sons);
    }

    public Node VarDef() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (line == 112) {
            int flag = 1;
        }
        if (this.current.getKind().equals(Pairs.Kind.IDENFR)) {
            sons.add(new Node(current, null));
            getSym();
            while (this.current.getKind().equals(Pairs.Kind.LBRACK)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(ConstExp());
                if (this.current.getKind().equals(Pairs.Kind.RBRACK)) {
                    sons.add(new Node(current, null));
                    getSym();
                    continue;
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "k");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                }
            }
            if (this.current.getKind().equals(Pairs.Kind.ASSIGN)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(InitVal());
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "VarDef", line);
        return new Node(pairs, sons);
    }

    public Node InitVal() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.LBRACE)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.RBRACE)) {
            } else {
                sons.add(InitVal());
                while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
                    sons.add(new Node(current, null));
                    getSym();
                    sons.add(InitVal());
                }
                if (this.current.getKind().equals(Pairs.Kind.RBRACE)) {
                    sons.add(new Node(current, null));
                    getSym();
                }
            }
        } else {
            sons.add(Exp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "InitVal", line);
        return new Node(pairs, sons);
    }

    public Node FuncDef() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (line == 59) {
            int flag = 1;
        }
        sons.add(FuncType());
        if (this.current.getKind().equals(Pairs.Kind.IDENFR)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
                sons.add(new Node(current, null));
                getSym();
                if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                    sons.add(new Node(current, null));
                    getSym();
                    sons.add(Block());
                } else {
                    if (this.current.getKind().equals(Pairs.Kind.INTTK)) {
                        sons.add(FuncFParams());
                        if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                            sons.add(new Node(current, null));
                            getSym();
                            sons.add(Block());
                        } else {
                            Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                            error.setLine(this.tokens.get(index - 2).getLine());
                            this.error.add(error);
                            sons.add(Block());
                        }
                    } else {
                        Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                        error.setLine(this.tokens.get(index - 2).getLine());
                        this.error.add(error);
                        sons.add(Block());
                    }
                }
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "FuncDef", line);
        return new Node(pairs, sons);
    }

    public Node MainFuncDef() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.INTTK)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.MAINTK)) {
                sons.add(new Node(current, null));
                getSym();
                if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
                    sons.add(new Node(current, null));
                    getSym();
                    if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                        sons.add(new Node(current, null));
                        getSym();
                        sons.add(Block());
                    } else {
                        Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                        error.setLine(this.tokens.get(index - 2).getLine());
                        this.error.add(error);
                        sons.add(Block());
                    }
                }
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "MainFuncDef", line);
        return new Node(pairs, sons);
    }

    public Node FuncType() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.VOIDTK) || this.current.getKind().equals(Pairs.Kind.INTTK)) {
            sons.add(new Node(current, null));
            getSym();
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "FuncType", line);
        return new Node(pairs, sons);
    }

    public Node FuncFParams() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(FuncFParam());
        while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
            sons.add(new Node(current, null));
            getSym();
            sons.add(FuncFParam());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "FuncFParams", line);
        return new Node(pairs, sons);
    }

    public Node FuncFParam() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(BType());
        if (this.current.getKind().equals(Pairs.Kind.IDENFR)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.LBRACK)) {
                sons.add(new Node(current, null));
                getSym();
                if (this.current.getKind().equals(Pairs.Kind.RBRACK)) {
                    sons.add(new Node(current, null));
                    getSym();
                    while (this.current.getKind().equals(Pairs.Kind.LBRACK)) {
                        sons.add(new Node(current, null));
                        getSym();
                        sons.add(ConstExp());
                        if (this.current.getKind().equals(Pairs.Kind.RBRACK)) {
                            sons.add(new Node(current, null));
                            getSym();
                            continue;
                        } else {
                            Pairs error = new Pairs(Pairs.Kind.ERROR, "k");
                            error.setLine(this.tokens.get(index - 2).getLine());
                            this.error.add(error);
                        }
                    }
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "k");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                    while (this.current.getKind().equals(Pairs.Kind.LBRACK)) {
                        sons.add(new Node(current, null));
                        getSym();
                        sons.add(ConstExp());
                        if (this.current.getKind().equals(Pairs.Kind.RBRACK)) {
                            sons.add(new Node(current, null));
                            getSym();
                            continue;
                        } else {
                            Pairs error1 = new Pairs(Pairs.Kind.ERROR, "k");
                            error1.setLine(this.tokens.get(index - 2).getLine());
                            this.error.add(error1);
                        }
                    }
                }
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "FuncFParam", line);
        return new Node(pairs, sons);
    }

    public Node Block() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.LBRACE)) {
            sons.add(new Node(current, null));
            getSym();
            while (!this.current.getKind().equals(Pairs.Kind.RBRACE)) {
                sons.add(BlockItem());
            }
            if (this.current.getKind().equals(Pairs.Kind.RBRACE)) {
                sons.add(new Node(current, null));
                getSym();
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "Block", line);
        return new Node(pairs, sons);
    }

    public Node BlockItem() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.CONSTTK) || this.current.getKind().equals(Pairs.Kind.INTTK)) {
            sons.add(Decl());
        } else {
            sons.add(Stmt());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "BlockItem", line);
        return new Node(pairs, sons);
    }

    public Node Stmt() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (line == 179) {
            int flag = 1;
        }
        if (this.current.getKind().equals(Pairs.Kind.IDENFR) && (this.next.getKind().equals(Pairs.Kind.ASSIGN)
                || this.next.getKind().equals(Pairs.Kind.LBRACK))) {
            int tempIndex = index - 1;
            int errorSize = error.size();
            sons.add(LVal());
            if (this.current.getKind().equals(Pairs.Kind.ASSIGN)) {
                sons.add(new Node(current, null));
                getSym();
                if (this.current.getKind().equals(Pairs.Kind.GETINTTK)) {
                    sons.add(new Node(current, null));
                    getSym();
                    if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
                        sons.add(new Node(current, null));
                        getSym();
                        if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                            sons.add(new Node(current, null));
                            getSym();
                            if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                                sons.add(new Node(current, null));
                                getSym();
                            } else {
                                Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                                error.setLine(this.tokens.get(index - 2).getLine());
                                this.error.add(error);
                            }
                        } else {
                            Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                            error.setLine(this.tokens.get(index - 2).getLine());
                            this.error.add(error);
                            if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                                sons.add(new Node(current, null));
                                getSym();
                            } else {
                                Pairs error1 = new Pairs(Pairs.Kind.ERROR, "i");
                                error1.setLine(this.tokens.get(index - 2).getLine());
                                this.error.add(error1);
                            }
                        }
                    }
                } else {
                    sons.add(Exp());
                    if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                        sons.add(new Node(current, null));
                        getSym();
                    } else {
                        Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                        error.setLine(this.tokens.get(index - 2).getLine());
                        this.error.add(error);
                    }
                }
            } else {        //exp;
                index = tempIndex;
                this.current = tokens.get(index);
                this.next = tokens.get(index + 1);
                this.nNext = tokens.get(index + 2);
                index++;
                int temp = sons.size();
                sons.remove(temp - 1);
                ArrayList<Pairs> tempError = new ArrayList<>();
                for (int i = errorSize;i<error.size();i++) {
                    tempError.add(error.get(i));
                }
                error.removeAll(tempError);
                sons.add(Exp());
                if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                    sons.add(new Node(current, null));
                    getSym();
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                }
            }
        } else if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {      //;
            sons.add(new Node(current, null));
            getSym();
        } else if (this.current.getKind().equals(Pairs.Kind.LBRACE)) {
            sons.add(Block());
        } else if (this.current.getKind().equals(Pairs.Kind.IFTK)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(Cond());
                if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                    sons.add(new Node(current, null));
                    getSym();
                    sons.add(Stmt());
                    if (this.current.getKind().equals(Pairs.Kind.ELSETK)) {
                        sons.add(new Node(current, null));
                        getSym();
                        sons.add(Stmt());
                    }
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                    sons.add(Stmt());
                    if (this.current.getKind().equals(Pairs.Kind.ELSETK)) {
                        sons.add(new Node(current, null));
                        getSym();
                        sons.add(Stmt());
                    }
                }
            }
        } else if (this.current.getKind().equals(Pairs.Kind.WHILETK)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(Cond());
                if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                    sons.add(new Node(current, null));
                    getSym();
                    sons.add(Stmt());
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                    sons.add(Stmt());
                }
            }
        } else if (this.current.getKind().equals(Pairs.Kind.BREAKTK) || this.current.getKind().equals(Pairs.Kind.CONTINUETK)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                sons.add(new Node(current, null));
                getSym();
            } else {
                Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                error.setLine(this.tokens.get(index - 2).getLine());
                this.error.add(error);
            }
        } else if (this.current.getKind().equals(Pairs.Kind.RETURNTK)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                sons.add(new Node(current, null));
                getSym();
            } else {
                if ((this.current.getKind().equals(Pairs.Kind.IDENFR) && this.next.getKind().equals(Pairs.Kind.LPARENT)) ||
                        (this.current.getKind().equals(Pairs.Kind.PLUS) || this.current.getKind().equals(Pairs.Kind.MINU) || this.current.getKind().equals(Pairs.Kind.NOT)) ||
                        (this.current.getKind().equals(Pairs.Kind.LPARENT) || this.current.getKind().equals(Pairs.Kind.INTCON) || this.current.getKind().equals(Pairs.Kind.IDENFR))) {
                    sons.add(Exp());
                    if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                        sons.add(new Node(current, null));
                        getSym();
                    } else {
                        Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                        error.setLine(this.tokens.get(index - 2).getLine());
                        this.error.add(error);
                    }
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                }
            }
        } else if (this.current.getKind().equals(Pairs.Kind.PRINTFTK)) {
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
                sons.add(new Node(current, null));
                getSym();
                if (this.current.getKind().equals(Pairs.Kind.STRCON)) {
                    sons.add(new Node(current, null));
                    getSym();
                    while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
                        sons.add(new Node(current, null));
                        getSym();
                        sons.add(Exp());
                    }
                    if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                        sons.add(new Node(current, null));
                        getSym();
                        if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                            sons.add(new Node(current, null));
                            getSym();
                        } else {
                            Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                            error.setLine(this.tokens.get(index - 2).getLine());
                            this.error.add(error);
                        }
                    } else {
                        Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                        error.setLine(this.tokens.get(index - 2).getLine());
                        this.error.add(error);
                        if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                            sons.add(new Node(current, null));
                            getSym();
                        } else {
                            Pairs error1 = new Pairs(Pairs.Kind.ERROR, "i");
                            error1.setLine(this.tokens.get(index - 2).getLine());
                            this.error.add(error1);
                        }
                    }
                }
            }
        } else {
            sons.add(Exp());
            if (this.current.getKind().equals(Pairs.Kind.SEMICN)) {
                sons.add(new Node(current, null));
                getSym();
            } else {
                Pairs error = new Pairs(Pairs.Kind.ERROR, "i");
                error.setLine(this.tokens.get(index - 2).getLine());
                this.error.add(error);
            }
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "Stmt", line);
        return new Node(pairs, sons);
    }

    public Node Exp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(AddExp());
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "Exp", line);
        return new Node(pairs, sons);
    }

    public Node Cond() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(LOrExp());
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "Cond", line);
        return new Node(pairs, sons);
    }

    public Node LVal() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.IDENFR)) {
            sons.add(new Node(current, null));
            getSym();
            while (this.current.getKind().equals(Pairs.Kind.LBRACK)) {
                sons.add(new Node(current, null));
                getSym();
                sons.add(Exp());
                if (this.current.getKind().equals(Pairs.Kind.RBRACK)) {
                    sons.add(new Node(current, null));
                    getSym();
                    continue;
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "k");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                }
            }
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "LVal", line);
            return new Node(pairs, sons);
        }
        return null;
    }

    public Node PrimaryExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.LPARENT)) {
            sons.add(new Node(current, null));
            getSym();
            sons.add(Exp());
            if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                sons.add(new Node(current, null));
                getSym();
                Pairs pairs = new Pairs(Pairs.Kind.PARSE, "PrimaryExp", line);
                return new Node(pairs, sons);
            }
        } else if (this.current.getKind().equals(Pairs.Kind.INTCON)) {
            sons.add(Number());
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "PrimaryExp", line);
            return new Node(pairs, sons);
        } else {
            sons.add(LVal());
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "PrimaryExp", line);
            return new Node(pairs, sons);
        }
        return null;
    }

    public Node Number() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.INTCON)) {
            sons.add(new Node(current, null));
            getSym();
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "Number", line);
            return new Node(pairs, sons);
        }
        return null;
    }

    public Node UnaryExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.IDENFR) && this.next.getKind().equals(Pairs.Kind.LPARENT)) {
            sons.add(new Node(current, null));
            getSym();
            sons.add(new Node(current, null));
            getSym();
            if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                sons.add(new Node(current, null));
                getSym();
                Pairs pairs = new Pairs(Pairs.Kind.PARSE, "UnaryExp", line);
                return new Node(pairs, sons);
            } else {
                if ((this.current.getKind().equals(Pairs.Kind.IDENFR) && this.next.getKind().equals(Pairs.Kind.LPARENT)) ||
                        (this.current.getKind().equals(Pairs.Kind.PLUS) || this.current.getKind().equals(Pairs.Kind.MINU) || this.current.getKind().equals(Pairs.Kind.NOT)) ||
                        (this.current.getKind().equals(Pairs.Kind.LPARENT) || this.current.getKind().equals(Pairs.Kind.INTCON) || this.current.getKind().equals(Pairs.Kind.IDENFR))) {
                    while (!this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                        sons.add(FuncRParams());
                        if ((this.current.getKind().equals(Pairs.Kind.IDENFR) && this.next.getKind().equals(Pairs.Kind.LPARENT)) ||
                                (this.current.getKind().equals(Pairs.Kind.PLUS) || this.current.getKind().equals(Pairs.Kind.MINU) || this.current.getKind().equals(Pairs.Kind.NOT)) ||
                                (this.current.getKind().equals(Pairs.Kind.LPARENT) || this.current.getKind().equals(Pairs.Kind.INTCON) || this.current.getKind().equals(Pairs.Kind.IDENFR))) {
                            continue;
                        } else {
                            break;
                        }
                    }
                    if (this.current.getKind().equals(Pairs.Kind.RPARENT)) {
                        sons.add(new Node(current, null));
                        getSym();
                    } else {
                        Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                        error.setLine(this.tokens.get(index - 2).getLine());
                        this.error.add(error);
                    }
                } else {
                    Pairs error = new Pairs(Pairs.Kind.ERROR, "j");
                    error.setLine(this.tokens.get(index - 2).getLine());
                    this.error.add(error);
                }
                Pairs pairs = new Pairs(Pairs.Kind.PARSE, "UnaryExp", line);
                return new Node(pairs, sons);
            }
        } else if (this.current.getKind().equals(Pairs.Kind.PLUS) || this.current.getKind().equals(Pairs.Kind.MINU) || this.current.getKind().equals(Pairs.Kind.NOT)) {
            sons.add(UnaryOp());
            sons.add(UnaryExp());
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "UnaryExp", line);
            return new Node(pairs, sons);
        } else {
            sons.add(PrimaryExp());
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "UnaryExp", line);
            return new Node(pairs, sons);
        }
    }

    public Node UnaryOp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        if (this.current.getKind().equals(Pairs.Kind.PLUS) || this.current.getKind().equals(Pairs.Kind.MINU) || this.current.getKind().equals(Pairs.Kind.NOT)) {
            sons.add(new Node(current, null));
            getSym();
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "UnaryOp", line);
            return new Node(pairs, sons);
        }
        return null;
    }

    public Node FuncRParams() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(Exp());
        while (this.current.getKind().equals(Pairs.Kind.COMMA)) {
            sons.add(new Node(current, null));
            getSym();
            sons.add(Exp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "FuncRParams", line);
        return new Node(pairs, sons);
    }

    public Node MulExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(UnaryExp());
        while (this.current.getKind().equals(Pairs.Kind.MULT) || this.current.getKind().equals(Pairs.Kind.DIV) || this.current.getKind().equals(Pairs.Kind.MOD)) {
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "MulExp", this.current.getLine());
            sons.add(new Node(pairs, null));
            sons.add(new Node(current, null));
            getSym();
            sons.add(UnaryExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "MulExp", line);
        return new Node(pairs, sons);
    }

    public Node AddExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(MulExp());
        while (this.current.getKind().equals(Pairs.Kind.PLUS) || this.current.getKind().equals(Pairs.Kind.MINU)) {
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "AddExp", this.current.getLine());
            sons.add(new Node(pairs, null));
            sons.add(new Node(current, null));
            getSym();
            sons.add(MulExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "AddExp", line);
        return new Node(pairs, sons);
    }

    public Node RelExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(AddExp());
        while (this.current.getKind().equals(Pairs.Kind.LSS) || this.current.getKind().equals(Pairs.Kind.LEQ) || this.current.getKind().equals(Pairs.Kind.GRE) || this.current.getKind().equals(Pairs.Kind.GEQ)) {
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "RelExp", this.current.getLine());
            sons.add(new Node(pairs, null));
            sons.add(new Node(current, null));
            getSym();
            sons.add(AddExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "RelExp", line);
        return new Node(pairs, sons);
    }

    public Node EqExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(RelExp());
        while (this.current.getKind().equals(Pairs.Kind.EQL) || this.current.getKind().equals(Pairs.Kind.NEQ)) {
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "EqExp", this.current.getLine());
            sons.add(new Node(pairs, null));
            sons.add(new Node(this.current, null));
            getSym();
            sons.add(RelExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "EqExp", line);
        return new Node(pairs, sons);
    }

    public Node LAndExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(EqExp());
        while (this.current.getKind().equals(Pairs.Kind.AND)) {
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "LAndExp", this.current.getLine());
            sons.add(new Node(pairs, null));
            sons.add(new Node(this.current, null));
            getSym();
            sons.add(EqExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "LAndExp", line);
        return new Node(pairs, sons);
    }

    public Node LOrExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(LAndExp());
        while (this.current.getKind().equals(Pairs.Kind.OR)) {
            Pairs pairs = new Pairs(Pairs.Kind.PARSE, "LOrExp", this.current.getLine());
            sons.add(new Node(pairs, null));
            sons.add(new Node(this.current, null));
            getSym();
            sons.add(LAndExp());
        }
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "LOrExp", line);
        return new Node(pairs, sons);
    }

    public Node ConstExp() {
        ArrayList<Node> sons = new ArrayList<>();
        int line = this.current.getLine();
        sons.add(AddExp());
        Pairs pairs = new Pairs(Pairs.Kind.PARSE, "ConstExp", line);
        return new Node(pairs, sons);
    }
}

